/*
 * @(#)IbatisDaoSupport.java
 *
 * Copyright 2012 by ShenY BIS, All rights reserved.
 */

package com.bis.framework.base.dao.ibatis;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ibatis.SqlMapClientCallback;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.bis.framework.base.dao.BaseEnityDao;
import com.bis.framework.base.entity.BaseEntity;
import com.bis.framework.util.common.COConstants;
import com.bis.framework.util.common.Page;
import com.bis.framework.util.common.PageData;
import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.ibatis.sqlmap.client.event.RowHandler;

/**
 * iBatis access base
 *
 * @author BIS
 * @version 1.0, 2007-08-23
 */
public abstract class IbatisDaoSupport<T> extends SqlMapClientDaoSupport implements BaseEnityDao<T>
{
    /**
     * 系统运行的日志记录器。
     */
    protected Logger logger = Logger.getLogger(getClass());

    /**
     * @return the logger
     */
    public Logger getLogger()
    {
        return logger;
    }

    /**
     * @param logger the logger to set
     */
    public void setLogger(Logger logger)
    {
        this.logger = logger;
    }

    /**
     * 返回sqlMap的命名空间。
     *
     * @return
     */
    public abstract String getNameSpace();

    /**
     * 1.通用的插入 <note>对应的sqlMap必须符合以下规范:</br>
     * <dl>
     * <li>Entity必须和数据库表中存在对应关系</li>
     * <li>sqlMap insert符合:"{tableName}.insertEntity"的命名规范,其中tableName是
     * Entity影射的表名称，同时是sqlMap的NameSpace</li>
     * </dl>
     * </note>
     *
     * @param entity
     */
    public void insertEntity(T entity)
    {
        this.getSqlMapClientTemplate().insert(getNameSpace() + "insertEntity", entity);
    }

    /**
     * 2.通用的插入 <note>对应的sqlMap必须符合以下规范:</br>
     * <dl>
     * <li>Entity必须和数据库表中存在对应关系</li>
     * <li>sqlMap insert符合:"{tableName}.insertEntity"的命名规范,其中tableName是
     * Entity影射的表名称，同时是sqlMap的NameSpace</li>
     * </dl>
     * </note>
     *
     * @param entity
     */
    public Long insertEntityReturnId(T entity)
    {
        return (Long) getSqlMapClientTemplate().insert(getNameSpace() + "insertEntity", entity);
    }

    /**
     * 3.根据给定的实体类列表保存都数据库中。
     *
     * @param entity
     */
    public void insertEntity(final List<T> entities)
    {
        getSqlMapClientTemplate().execute(new SqlMapClientCallback()
        {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException
            {
                executor.startBatch();
                int size = entities.size();
                for (int i = 0; i < size; i++)
                {
                    executor.insert(getNameSpace() + "insertEntity", entities.get(i));
                }
                executor.executeBatch();
                return null;
            }
        });
    }

    /**
     * 4.根据给定的实体类查询数据列表。
     */
    public List<T> selectByEntity(BaseEntity sourceObj)
    {
        return (List<T>) this.getSqlMapClientTemplate().queryForList(getNameSpace(), sourceObj);
    }

    /**
     * 5.根据给定的主键查询数据。
     *
     * @param key
     * @return Entity
     */
    public T selectByPrimaryKey(Object key)
    {
        return (T) this.getSqlMapClientTemplate().queryForObject(getNameSpace() + "selectByPrimaryKey", key);
    }

    /**
     * 6.根据给定的条件列表查询数据列表。
     *
     * @param map
     * @return Entity
     */
    public List<T> selectByMap(Map<String, Object> map)
    {
        return this.getSqlMapClientTemplate().queryForList(getNameSpace() + "selectByMap", map);
    }

    /**
     * 根据给定的条件列表查询分页数据。
     *
     * @param statementCount
     * @param statementPage
     * @param map
     * @param page
     * @throws DataAccessException
     */
    protected PageData selectByMap(String statementCount, String statementPage, Map map, Page page)
    {

        Integer count = (Integer) super.getSqlMapClientTemplate().queryForObject(statementCount, map);
        page.setRowCount(count.intValue());
        if (map == null)
        {
            map = new HashMap();
        }

        int pageSize = page.getPageSize();
        int rowCount = page.getRowCount();
        int currPage = page.getPage();
        if (rowCount % pageSize == 0)
        {
            if (currPage > rowCount / pageSize || currPage <= 0)
                currPage = rowCount / pageSize;
        }
        else
        {
            if (currPage > rowCount / pageSize + 1 || currPage <= 0)
                currPage = rowCount / pageSize + 1;
        }

        if (currPage < 1)
            currPage = 1;

        int start = (currPage - 1) * pageSize + 1;

        int topCount = start + pageSize - 1;
        if (rowCount < topCount)
        {
            pageSize = rowCount - start + 1;
            topCount = rowCount;
        }

        if (pageSize <= 0)
            return new PageData(page, new ArrayList());

        map.put("page", page.getPage());
        map.put("pageSize", page.getPageSize());
        map.put("offset", start - 1);
        map.put("startPos", start);
        map.put("endPos", topCount);
        return new PageData(page, this.getSqlMapClientTemplate().queryForList(statementPage, map));
    }

    /**
     * 7.根据给定的条件列表查询分页数据。
     *
     * @param entity
     *
     */
    public PageData selectByMap(Map map, Page page)
    {
        return selectByMap(getNameSpace() + "selectCount", getNameSpace() + "selectWithPage", map, page);
    }

    /**
     * 8.根据给定的条件列表，查询数据总数。
     *
     * @param conditionMap
     */
    public int selectCountByMap(Map<String, Object> conditionMap)
    {
        return (Integer) this.getSqlMapClientTemplate().queryForObject(getNameSpace() + "selectCountByMap", conditionMap);
    }
    
    /**
     * 9.通用的根据主键更新 <note>对应的sqlMap必须符合以下规范:</br>
     * <dl>
     * <li>entiy必须和数据库表中存在对应关系</li>
     * <li>sqlMap update符合:"{tableName}.updateEntity"的命名规范,其中tableName是
     * enity影射的表名称，同时是sqlMap的namespace</li>
     * </dl>
     * </note>
     *
     * @param entiy
     * @return 影响的行数
     */
    public void updateEntity(T entity)
    {
        this.getSqlMapClientTemplate().update(getNameSpace() + "updateEntity", entity);
    }

    /**
     * 10.根据给定的实体类列表更新数据库。
     *
     * @param entiy
     */
    public void updateEntity(final List<T> entities)
    {
        getSqlMapClientTemplate().execute(new SqlMapClientCallback()
        {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException
            {
                executor.startBatch();
                int size = entities.size();
                for (int i = 0; i < size; i++)
                {
                    executor.update(getNameSpace() + "updateEntity", entities.get(i));
                }
                executor.executeBatch();
                return null;
            }
        });
    }



    /**
     * 11.根据给定的主键删除数据。
     *
     * @param key
     */
    public void deleteByPrimaryKey(Object key)
    {
        this.getSqlMapClientTemplate().delete(getNameSpace() + "deleteByPrimaryKey", key);
    }

    /**
     * 12.根据给定的主键列表删除数据。
     *
     * @param key
     */
    public void deleteByPrimaryKey(final List keys)
    {
        getSqlMapClientTemplate().execute(new SqlMapClientCallback()
        {
            public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException
            {
                executor.startBatch();
                int size = keys.size();
                for (int i = 0; i < size; i++)
                {
                    executor.delete(getNameSpace() + "deleteByPrimaryKey", keys.get(i));
                }
                executor.executeBatch();
                return null;
            }
        });
    }

    /**
     * 13.根据给定的条件列表对数据进行删除处理。
     *
     * @param map
     *
     */
    public void deleteByMap(Map map)
    {
        try
        {
            this.getSqlMapClientTemplate().delete(getNameSpace() + "deleteByMap", map);
        }
        catch (Throwable e)
        {
            String msg = e.getMessage();
            if (msg != null)
            {
                if (msg.indexOf("ORA-02292") > -1)
                {
                    throw new RuntimeException("该记录已经被引用,不允许删除!");
                }
                else
                {
                    throw new RuntimeException(e);
                }
            }
            else
            {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 处理行查询触发器。
     *
     * @param statementName
     * @param handler
     * @throws DataAccessException
     */
    protected void selectWithRowHandler(String statementName, final com.bis.framework.base.dao.event.RowHandler handler) throws DataAccessException
    {

        handler.prepare();
        RowHandler h = new RowHandler()
        {
            public void handleRow(Object valueObject)
            {
                handler.handleRow(valueObject);
            }
        };
        super.getSqlMapClientTemplate().queryWithRowHandler(statementName, h);
        handler.complete();
    }

    /**
     * 处理行查询触发器。
     *
     * @param statementName
     * @param map
     * @param handler
     * @throws DataAccessException
     */
    protected void selectWithRowHandler(String statementName, Map map, final com.bis.framework.base.dao.event.RowHandler handler) throws DataAccessException
    {

        handler.prepare();
        RowHandler h = new RowHandler()
        {
            public void handleRow(Object valueObject)
            {
                handler.handleRow(valueObject);
            }
        };
        super.getSqlMapClientTemplate().queryWithRowHandler(statementName, map, h);
        handler.complete();

    }

    /**
     * 根据给定的条件列表用存储过程进行数据查询。
     *
     * @param procedureId
     * @param map
     * @param page
     * @throws DataAccessException
     */
    protected PageData selectByPagingProcedure(String procedureId, Map map, Page page)
    {
        page.setPageSize(COConstants.PAGE_COUNT);
        this.getSqlMapClientTemplate().queryForList(procedureId, map);
        List<T> rtnList = (List<T>) map.get("resultList");
        String resultCnt = String.valueOf(map.get("irowCount"));
        if ("null".equals(resultCnt) || "".equals(resultCnt))
        {
            page.setRowCount(1);
        }
        else
        {
            page.setRowCount(Integer.valueOf(resultCnt));
        }
        return new PageData(page, rtnList);
    }

    /**
     * 根据给定的条件列表用存储过程进行数据查询。
     *
     * @param procedureId
     * @param map
     * @param page
     * @throws DataAccessException
     */
    protected List<T> selectByNormalprocedure(String procedureId, Map map)
    {
        this.getSqlMapClientTemplate().queryForList(procedureId, map);
        List<T> rtnList = (List<T>) map.get("resultList");
        return rtnList;
    }
}
